home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / lib / tclX-6.4 / buildhelp.tcl < prev    next >
Encoding:
Text File  |  1992-12-17  |  14.4 KB  |  440 lines

  1. #
  2. # buildhelp.tcl --
  3. #
  4. # Program to extract help files from TCL manual pages or TCL script files.
  5. # The help directories are built as a hierarchical tree of subjects and help
  6. # files.  
  7. #------------------------------------------------------------------------------
  8. # Copyright 1992 Karl Lehenbauer and Mark Diekhans.
  9. #
  10. # Permission to use, copy, modify, and distribute this software and its
  11. # documentation for any purpose and without fee is hereby granted, provided
  12. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  13. # Mark Diekhans make no representations about the suitability of this
  14. # software for any purpose.  It is provided "as is" without express or
  15. # implied warranty.
  16. #------------------------------------------------------------------------------
  17. # $Id: buildhelp.tcl,v 2.1 1992/10/25 17:07:40 markd Exp $
  18. #------------------------------------------------------------------------------
  19. #
  20. # For nroff man pages, the areas of text to extract are delimited with:
  21. #
  22. #     '@help: subjectdir/helpfile
  23. #     '@endhelp
  24. #
  25. # start in column one. The text between these markers is extracted and stored
  26. # in help/subjectdir/help.  The file must not exists, this is done to enforced 
  27. # cleaning out the directories before help file generation is started, thus
  28. # removing any stale files.  The extracted text is run through:
  29. #
  30. #     nroff -man|col -xb   {col -b on BSD derived systems}
  31. #
  32. # If there is other text to include in the helpfile, but not in the manual 
  33. # page, the text, along with nroff formatting commands, may be included using:
  34. #
  35. #     '@:Other text to include in the help page.
  36. #
  37. # A entry in the brief file, used by apropos my be included by:
  38. #
  39. #     '@brief: Short, one line description
  40. #
  41. # These brief request must occur with in the bounds of a help section.
  42. #
  43. # If some header text, such as nroff macros, need to be preappended to the
  44. # text streem before it is run through nroff, then that text can be bracketed
  45. # with:
  46. #
  47. #     '@header
  48. #     '@endheader
  49. #
  50. # If multiple header blocks are encountered, they will all be preappended.
  51. #
  52. # For TCL script files, which are indentified because they end in ".tcl",
  53. # the text to be extracted is delimited by:
  54. #
  55. #    #@help: subjectdir/helpfile
  56. #    #@endhelp
  57. #
  58. # And brief lines are in the form:
  59. #
  60. #     #@brief: Short, one line description
  61. #
  62. # The only processing done on text extracted from .tcl files it to replace
  63. # the # in column one with a space.
  64. #
  65. #
  66. #-----------------------------------------------------------------------------
  67. # To run this program:
  68. #
  69. #   tcl buildhelp.tcl [-m mergeTree] -b brief.brf helpDir file-1 file-2 ...
  70. #
  71. # o -m mergeTree is a tree of help code, plus a brief file to merge with the
  72. #   help files that are to be extracted.  This will become part of the new
  73. #   help tree.  Used to merge in the documentation from UCB Tcl.
  74. # o -b specified the name of the brief file to create form the @brief entries.
  75. #   It must have an extension of ".brf".
  76. # o helpDir is the help tree root directory.  helpDir should  exists, but any
  77. #   subdirectories that don't exists will be created.  helpDir should be
  78. #   cleaned up before the start of manual page generation, as this program
  79. #   will not overwrite existing files.
  80. # o file-n are the nroff manual pages (.man) or .tcl or .tlib files to extract
  81. #   the help files from.
  82. #
  83. #-----------------------------------------------------------------------------
  84.  
  85. #-----------------------------------------------------------------------------
  86. # Truncate a file name of a help file if the system does not support long
  87. # file names.  If the name starts with `Tcl_', then this prefix is removed.
  88. # If the name is then over 14 characters, it is truncated to 14 charactes
  89. #  
  90. proc TruncFileName {pathName} {
  91.     global G_truncFileNames
  92.  
  93.     if {!$G_truncFileNames} {
  94.         return $pathName}
  95.     set fileName [file tail $pathName]
  96.     if {"[crange $fileName 0 3]" == "Tcl_"} {
  97.         set fileName [crange $fileName 4 end]}
  98.     set fileName [crange $fileName 0 13]
  99.     return "[file dirname $pathName]/$fileName"
  100. }
  101.  
  102. #-----------------------------------------------------------------------------
  103. # Proc to ensure that all directories for the specified file path exists,
  104. # and if they don't create them.  Don't use -path so we can set the
  105. # permissions.
  106.  
  107. proc EnsureDirs {filePath} {
  108.     set dirPath [file dirname $filePath]
  109.     if [file exists $dirPath] return
  110.     foreach dir [split $dirPath /] {
  111.         lappend dirList $dir
  112.         set partPath [join $dirList /]
  113.         if [file exists $partPath] continue
  114.  
  115.         mkdir $partPath
  116.         chmod u=rwx,go=rx $partPath
  117.     }
  118. }
  119.  
  120.  
  121. #-----------------------------------------------------------------------------
  122. #
  123. # Proc to extract nroff text to use as a header to all pass to nroff when
  124. # processing a help file.
  125. #    manPageFH - The file handle of the manual page.
  126. #
  127.  
  128. proc ExtractNroffHeader {manPageFH} {
  129.     global nroffHeader
  130.     while {[gets $manPageFH manLine] >= 0} {
  131.         if {[string first "'@endheader" $manLine] == 0} {
  132.             break;
  133.             }
  134.         if {[string first "'@:" $manLine] == 0} {
  135.             set manLine [csubstr manLine 3 end]
  136.             }
  137.         append nroffHeader "$manLine\n"
  138.         }
  139. }
  140.  
  141. #-----------------------------------------------------------------------------
  142. #
  143. # Proc to extract a nroff help file when it is located in the text.
  144. #    manPageFH - The file handle of the manual page.
  145. #    manLine - The '@help: line starting the data to extract.
  146. #
  147.  
  148. proc ExtractNroffHelp {manPageFH manLine} {
  149.     global G_helpDir nroffHeader G_briefHelpFH G_colArgs
  150.  
  151.     set helpName [string trim [csubstr $manLine 7 end]]
  152.     set helpFile [TruncFileName "$G_helpDir/$helpName"]
  153.     if {[file exists $helpFile]} {
  154.         error "Help file already exists: $helpFile"}
  155.     EnsureDirs $helpFile
  156.     set helpFH [open "| nroff -man | col $G_colArgs > $helpFile" w]
  157.     echo "    creating help file $helpName"
  158.  
  159.     # Nroff commands from .TH macro to get the formatting right.  The `\n'
  160.     # are newline separators to output, the `\\n' become `\n' in the text.
  161.         
  162.     puts $helpFH ".ad b\n.PD\n.nrIN \\n()Mu\n.nr)R 0\n.nr)I \\n()Mu"
  163.     puts $helpFH ".nr)R 0\n.\}E\n.DT\n.na\n.nh"
  164.     puts $helpFH $nroffHeader
  165.     set foundBrief 0
  166.     while {[gets $manPageFH manLine] >= 0} {
  167.         if {[string first "'@endhelp" $manLine] == 0} {
  168.             break;
  169.         }
  170.         if {[string first "'@brief:" $manLine] == 0} {
  171.             if $foundBrief {
  172.                 error {Duplicate "'@brief" entry"}
  173.             }
  174.             set foundBrief 1
  175.         puts $G_briefHelpFH "$helpName\t[csubstr $manLine 8 end]"
  176.             continue;
  177.         }
  178.         if {[string first "'@:" $manLine] == 0} {
  179.             set manLine [csubstr $manLine 3 end]
  180.         }
  181.         if {[string first "'@help" $manLine] == 0} {
  182.             error {"'@help" found within another help section"}
  183.         }
  184.         puts $helpFH $manLine
  185.         }
  186.     close $helpFH
  187.     chmod a-w,a+r $helpFile
  188. }
  189.  
  190. #-----------------------------------------------------------------------------
  191. #
  192. # Proc to extract a tcl script help file when it is located in the text.
  193. #    ScriptPageFH - The file handle of the .tcl file.
  194. #    ScriptLine - The #@help: line starting the data to extract.
  195. #
  196.  
  197. proc ExtractScriptHelp {ScriptPageFH ScriptLine} {
  198.     global G_helpDir G_briefHelpFH
  199.     set helpName [string trim [csubstr $ScriptLine 7 end]]
  200.     set helpFile "$G_helpDir/$helpName"
  201.     if {[file exists $helpFile]} {
  202.         error "Help file already exists: $helpFile"}
  203.     EnsureDirs $helpFile
  204.     set helpFH [open $helpFile w]
  205.     echo "    creating help file $helpName"
  206.     set foundBrief 0
  207.     while {[gets $ScriptPageFH ScriptLine] >= 0} {
  208.         if {[string first "#@endhelp" $ScriptLine] == 0} {
  209.             break;
  210.         }
  211.         if {[string first "#@brief:" $ScriptLine] == 0} {
  212.             if $foundBrief {
  213.                 error {Duplicate "#@brief" entry"}
  214.             }
  215.             set foundBrief 1
  216.         puts $G_briefHelpFH "$helpName\t[csubstr $ScriptLine 8 end]"
  217.             continue;
  218.         }
  219.         if {[string first "#@help" $ScriptLine] == 0} {
  220.             error {"#@help" found within another help section"}
  221.         }
  222.         if {[clength $ScriptLine] > 1} {
  223.             set ScriptLine " [csubstr $ScriptLine 1 end]"
  224.         } else {
  225.             set ScriptLine ""
  226.         }
  227.         puts $helpFH $ScriptLine
  228.         }
  229.     close $helpFH
  230.     chmod a-w,a+r $helpFile
  231. }
  232.  
  233. #-----------------------------------------------------------------------------
  234. #
  235. # Proc to scan a nroff manual file looking for the start of a help text
  236. # sections and extracting those sections.
  237. #    pathName - Full path name of file to extract documentation from.
  238. #
  239.  
  240. proc ProcessNroffFile {pathName} {
  241.    global G_nroffScanCT G_scriptScanCT nroffHeader
  242.  
  243.    set fileName [file tail $pathName]
  244.  
  245.    set nroffHeader {}
  246.    set manPageFH [open $pathName r]
  247.    echo "    scanning $pathName"
  248.    set matchInfo(fileName) [file tail $pathName]
  249.    scanfile $G_nroffScanCT $manPageFH
  250.    close $manPageFH
  251. }
  252.  
  253. #-----------------------------------------------------------------------------
  254. #
  255. # Proc to scan a Tcl script file looking for the start of a
  256. # help text sections and extracting those sections.
  257. #    pathName - Full path name of file to extract documentation from.
  258. #
  259.  
  260. proc ProcessTclScript {pathName} {
  261.    global G_scriptScanCT nroffHeader
  262.  
  263.    set scriptFH [open "$pathName" r]
  264.  
  265.    echo "    scanning $pathName"
  266.    set matchInfo(fileName) [file tail $pathName]
  267.    scanfile $G_scriptScanCT $scriptFH
  268.    close $scriptFH
  269. }
  270.  
  271. #-----------------------------------------------------------------------------
  272. # Proc to copy the help merge tree, excluding the brief file and RCS files
  273.  
  274. proc CopyMergeTree {helpDirPath mergeTree} {
  275.     if {"[cindex $helpDirPath 0]" != "/"} {
  276.         set helpDirPath "[pwd]/$helpDirPath"
  277.     }
  278.     set oldDir [pwd]
  279.     cd $mergeTree
  280.  
  281.     set curHelpDir "."
  282.  
  283.     for_recursive_glob mergeFile {.} {
  284.         if [string match "*/RCS/*" $mergeFile] continue
  285.  
  286.         set helpFile "$helpDirPath/$mergeFile"
  287.         if [file isdirectory $mergeFile] continue
  288.  
  289.         if {[file exists $helpFile]} {
  290.             error "Help file already exists: $helpFile"}
  291.         EnsureDirs $helpFile
  292.         set inFH [open $mergeFile r]
  293.         set outFH [open $helpFile w]
  294.         copyfile $inFH $outFH
  295.         close $outFH
  296.         close $inFH
  297.         chmod a-w,a+r $helpFile
  298.     }
  299.     cd $oldDir
  300. }
  301.  
  302. #-----------------------------------------------------------------------------
  303. # GenerateHelp: main procedure.  Generates help from specified files.
  304. #    helpDirPath - Directory were the help files go.
  305. #    mergeTree - Help file tree to merge with the extracted help files.
  306. #    briefFile - The name of the brief file to create.
  307. #    sourceFiles - List of files to extract help files from.
  308.  
  309. proc GenerateHelp {helpDirPath briefFile mergeTree sourceFiles} {
  310.     global G_helpDir G_truncFileNames G_nroffScanCT
  311.     global G_scriptScanCT G_briefHelpFH G_colArgs
  312.  
  313.     echo ""
  314.     echo "Begin building help tree"
  315.  
  316.     # Determine version of col command to use (no -x on BSD)
  317.     if {[system {col -bx </dev/null >/dev/null 2>&1}] != 0} {
  318.         set G_colArgs {-b}
  319.     } else {
  320.         set G_colArgs {-bx}
  321.     }
  322.     set G_helpDir [glob $helpDirPath]
  323.  
  324.     if {![file isdirectory $G_helpDir]} {
  325.         error [concat "$G_helpDir is not a directory or does not exist. "  
  326.                       "This should be the help root directory"]
  327.     }
  328.         
  329.     set status [catch {set tmpFH [open $G_helpDir/AVeryVeryBigFileName w]}]
  330.     if {$status != 0} {
  331.         set G_truncFileNames 1
  332.     } else {
  333.         close $tmpFH
  334.         unlink $G_helpDir/AVeryVeryBigFileName
  335.         set G_truncFileNames 0
  336.     }
  337.  
  338.     set G_nroffScanCT [scancontext create]
  339.  
  340.     scanmatch $G_nroffScanCT "^'@help:" {
  341.         ExtractNroffHelp $matchInfo(handle) $matchInfo(line)
  342.         continue
  343.     }
  344.  
  345.     scanmatch $G_nroffScanCT "^'@header" {
  346.         ExtractNroffHeader $matchInfo(handle)
  347.         continue
  348.     }
  349.     scanmatch $G_nroffScanCT "^'@endhelp" {
  350.         error [concat {"'@endhelp" without corresponding "'@help:"} \
  351.                  ", offset = $matchInfo(offset)"]
  352.     }
  353.     scanmatch $G_nroffScanCT "^'@brief" {
  354.         error [concat {"'@brief" without corresponding "'@help:"}
  355.                  ", offset = $matchInfo(offset)"]
  356.     }
  357.  
  358.     set G_scriptScanCT [scancontext create]
  359.     scanmatch $G_scriptScanCT "^#@help:" {
  360.         ExtractScriptHelp $matchInfo(handle) $matchInfo(line)
  361.     }
  362.  
  363.     if ![lempty $mergeTree] {
  364.         echo "    Merging tree: $mergeTree"
  365.         CopyMergeTree $helpDirPath $mergeTree
  366.     }
  367.  
  368.  
  369.     if {[file extension $briefFile] != ".brf"} {
  370.         puts stderr "Brief file \"$briefFile\" must have an extension \".brf\""
  371.         exit 1
  372.     }
  373.     if [file exists $G_helpDir/$briefFile] {
  374.         puts stderr "Brief file \"$G_helpDir/$briefFile\" already exists"
  375.         exit 1
  376.     }
  377.     set G_briefHelpFH [open "|sort > $G_helpDir/$briefFile" w]
  378.  
  379.     foreach manFile $sourceFiles {
  380.         set manFile [glob $manFile]
  381.         set ext [file extension $manFile]
  382.         if {"$ext" == ".man"} {
  383.             set status [catch {ProcessNroffFile $manFile} msg]
  384.         } else {
  385.             set status [catch {ProcessTclScript $manFile} msg]
  386.         }
  387.         if {$status != 0} {
  388.             echo "Error extracting help from: $manFile"
  389.             echo $msg
  390.             global errorInfo interactiveSession
  391.             if {!$interactiveSession} {
  392.                 echo $errorInfo
  393.                 exit 1
  394.             }
  395.         }
  396.     }
  397.  
  398.     close $G_briefHelpFH
  399.     chmod a-w,a+r $G_helpDir/$briefFile
  400.     echo "*** completed extraction of all help files"
  401. }
  402.  
  403. #-----------------------------------------------------------------------------
  404. # Print a usage message and exit the program
  405. proc Usage {} {
  406.     puts stderr {Wrong args: [-m mergetree] -b briefFile helpdir manfile1 [manfile2..]}
  407.     exit 1
  408. }
  409.  
  410. #-----------------------------------------------------------------------------
  411. # Main program body, decides if help is interactive or batch.
  412.  
  413. if {$interactiveSession} {
  414.     echo "To extract help, use the command:"
  415.     echo {GenerateHelp helpdir -m mergetree file-1 file-2 ...}
  416. } else {
  417.     set mergeTree {}
  418.     set briefFile {}
  419.     while {[string match "-*" [lindex $argv 0]]} {
  420.         set flag [lvarpop argv 0]
  421.         case $flag in {
  422.             "-m" {set mergeTree [lvarpop argv]}
  423.             "-b" {set briefFile [lvarpop argv]}
  424.             default Usage
  425.         }
  426.     }
  427.     if {[llength $argv] < 2} {
  428.         Usage
  429.     }
  430.     if [lempty $briefFile] {
  431.        puts stderr {must specify -b argument}
  432.        Usage 
  433.     }
  434.     GenerateHelp [lindex $argv 0] $briefFile $mergeTree [lrange $argv 1 end]
  435.    
  436. }
  437.